package middleware

import (
	"context"
	"errors"
	"fmt"
	"github.com/dgrijalva/jwt-go"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/net/ghttp"
	"github.com/gogf/gf/v2/os/gtime"
	"net/http"
	"strconv"
	"time"
	"video-api/internal/consts"
	"video-api/utility"
	"video-api/utility/redis_client"
)

var LOGIN_TOKEN = "login_token:"
var secretKey = []byte("db35927424c04136b134bedc96e2138c")

type ClaimsInfo struct {
	UserId           string  `json:"userId"`
	UserCode         string  `json:"userCode" description:"第三方用户id"`
	Avatar           string  `json:"avatar,omitempty" description:"头像"`
	Email            string  `json:"email" description:"邮箱"`
	NickName         string  `json:"nickName" description:"昵称"`
	Token            string  `json:"token,omitempty"  description:"token"`
	UserRegisterTime float64 `json:"userRegisterTime" description:""`
	UserLoginType    string  `json:"userLoginType"    description:"登录类型 FB、GOOGLE、DAFAULT（系统登录）等"`
	UserSource       string  `json:"userSource"       description:"用户来源 THROW_IN：投放，NATURAL：自然流"`
	Country          string  `json:"country"   description:"国家"`
	IsLogin          int     `json:"isLogin" description:"是否登录（1：登录，2：登出）"`
	//jwt.StandardClaims
}

func GenerateToken(ctx context.Context, claimsInfo ClaimsInfo) (string, error) {
	// SigningMethodHS256
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
		"userId":           claimsInfo.UserId,
		"userCode":         claimsInfo.UserCode,
		"avatar":           claimsInfo.Avatar,
		"email":            claimsInfo.Email,
		"nickName":         claimsInfo.NickName,
		"userSource":       claimsInfo.UserSource,
		"userRegisterTime": claimsInfo.UserRegisterTime,
		"userLoginType":    claimsInfo.UserLoginType,
		"country":          claimsInfo.Country,
		"exp":              time.Now().Add(time.Hour * 24).Unix(),
	})
	tokenStr, err := token.SignedString(secretKey)
	if err != nil {
		return "", err
	}
	// 存入redis
	err = redis_client.RedisClient.Set(ctx, LOGIN_TOKEN+claimsInfo.UserId, tokenStr, time.Hour*24).Err()
	if err != nil {
		return "", err
	}
	return tokenStr, nil
}

func ValidateToken(tokenString string) (bool, *ClaimsInfo, error) {
	token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, fmt.Errorf("unexpected signing method")
		}
		return secretKey, nil
	})
	if err != nil {
		return false, nil, err
	}
	if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
		// Token是否过期
		if !claims.VerifyExpiresAt(time.Now().Unix(), false) {
			return false, nil, errors.New("access token expired")
		}
		return true, &ClaimsInfo{
			UserId:           claims["userId"].(string),
			UserCode:         claims["userCode"].(string),
			Avatar:           claims["avatar"].(string),
			Email:            claims["email"].(string),
			NickName:         claims["nickName"].(string),
			UserSource:       claims["userSource"].(string),
			UserLoginType:    claims["userLoginType"].(string),
			UserRegisterTime: claims["userRegisterTime"].(float64),
			Country:          claims["country"].(string),
		}, nil
	} else {
		return false, nil, nil
	}
}

// JwtKeyFunc jwtKeyFunc 返回密钥
func JwtKeyFunc(token *jwt.Token) (interface{}, error) {
	return secretKey, nil
}

type UserInfo struct {
	UserId           int64       `json:"userId"`
	UserCode         string      `json:"userCode" description:"第三方用户id"`
	Avatar           string      `json:"avatar,omitempty" description:"头像"`
	Email            string      `json:"email" description:"邮箱"`
	NickName         string      `json:"nickName" description:"昵称"`
	UserRegisterTime *gtime.Time `json:"userRegisterTime" description:""`
	UserLoginType    string      `json:"userLoginType"    description:"登录类型 FB、GOOGLE、DAFAULT（系统登录）等"`
	UserSource       string      `json:"userSource"       description:"用户来源 THROW_IN：投放，NATURAL：自然流"`
	Country          string      `json:"country"   description:"国家"`
}

// GetUserInfo 从context中获取用户信息
func GetUserInfo(ctx context.Context) *UserInfo {
	info := ctx.Value(consts.USER_JWT_KEY).(*ClaimsInfo)
	userId, _ := strconv.Atoi(info.UserId)
	return &UserInfo{
		UserId:           int64(userId),
		UserCode:         info.UserCode,
		Avatar:           info.Avatar,
		Email:            info.Email,
		NickName:         info.NickName,
		UserRegisterTime: gtime.NewFromTime(time.Unix(int64(info.UserRegisterTime), 0)),
		UserLoginType:    info.UserLoginType,
		UserSource:       info.UserSource,
		Country:          info.Country,
	}
}

func getUserInfoByToken(token string) (data *ClaimsInfo, err error) {
	// 验证jwt
	valid, userInfo, err := ValidateToken(token)
	if err != nil {
		g.Log().Error(context.TODO(), "getUserInfoByToken ValidateToken is err, ", err)
		return nil, err
	}
	if !valid {
		return nil, errors.New("token is invalid")
	}
	// 再次验证redis是否存在token
	tku, err := redis_client.RedisClient.Get(context.TODO(), LOGIN_TOKEN+userInfo.UserId).Result()
	if err != nil {
		g.Log().Error(context.TODO(), "getUserInfoByToken GetResult is err, ", err)
		return nil, err
	}
	if tku != token {
		return nil, errors.New("token is invalid")
	}
	return userInfo, nil
}

// JwtMiddleware JWT处理中间件
func JwtMiddleware(r *ghttp.Request) {
	if r.Request.RequestURI == "/api/v1/gi18nLang/lang_write" {
		r.Middleware.Next()
	} else if r.Request.URL.Path == "/api/v1/order/pay" {
		r.Middleware.Next()
	} else if r.Request.URL.Path == "/api/v1/user/create" {
		r.Middleware.Next()
	} else {
		token := utility.Param(r.Request, "token")
		userInfo, err := getUserInfoByToken(token)
		if err != nil {
			g.Log().Error(r.Context(), "未能通过token获取正确的用户信息", err)
			//r.Response.WriteStatus(http.StatusUnauthorized)
			r.Response.WriteJson(struct {
				Code    int         `json:"code"`
				Message string      `json:"message"`
				Data    interface{} `json:"data"`
			}{
				Code:    http.StatusUnauthorized,
				Message: "未能通过token获取正确的用户信息",
				Data:    nil,
			})
			return
		}
		ctx := r.Context()
		ctx = context.WithValue(ctx, "token", token)
		ctx = context.WithValue(ctx, consts.USER_JWT_KEY, userInfo)
		r.Request = r.Request.WithContext(ctx)
		r.Middleware.Next()
	}

}
